一个轻量级分布式 RPC 框架 下
3,编写客户端模块
设定Client
端的实现功能接口
1 | package com.nia.rpc.core.client; |
实现 RPC 代理
因会用到动态代理,那先设计动态代理实现的
1 | package com.nia.rpc.core.rpcproxy; |
这里使用CGLib 来实现 RPC 代理(当然也可以使用Java 提供的动态代理技术实现 ),具体代码如下:
1 | package com.nia.rpc.core.rpcproxy; |
客户端handler设计
在服务端完成处理对服务端的返回信息进行封装:
1 | package com.nia.rpc.core.utils; |
然后再客户端对返回信息进行处理:
因为代码注释很详细,细节就不说了
1 | package com.nia.rpc.core.client; |
客户端创建连接并保存
commons.pool2 对象池的使用
在客户端这里建立个对象池来保存并复用和提供服务端连接channel的信息,并在这里进行创建客户端连接然后保存,减少重复创建所带来的资源损耗
关于此对象池,想进一步了解,请看commons.pool2 对象池的使用,这里就不多说了
通过上个说明文档链接先搞个池对象工厂的
1 | package com.nia.rpc.core.utils; |
然后做个channel
包装:
主要还是为了得到一个池GenericObjectPool
:
1 | package com.nia.rpc.core.client; |
对客户端接口逻辑进行实现:
在客户端启动时通过init()
做如下逻辑:
- 通过
CuratorFrameworkFactory
来连接zk
; - 通过
curator API
的Path Cache
用来监控一个ZNode
的子节点. 这里是pathChildrenCache
,然后开写监控逻辑(方便动态的增加删除channel
):- 先在之前拿到
zk
下的所有节点,然后根据服务注册规则,所得到拼接服务的节点的key
,就可以得到提供服务的所有子节点list,这里是newServiceData
- 关闭删除本地缓存中多出的
channel
:- 通过遍历
channelWrappers
来判断newServiceData
是否包含有其中的链接,没包含的就清理掉,关闭相应对象池,并从channelWrappers
移除;
- 通过遍历
- 增加
channelWrappers
中未存在的服务连接channel
- 先在之前拿到
- 回到主线程,通过
List<String> strings = children.forPath(serviceZKPath);
得到提供服务的所有子节点list strings
为空,则报出无可用服务的运行时异常处理;- 不为空,添加进
channelWrappers
中
在客户端调用接口方法时实现的逻辑:
- 对接口方法调用进行
request
包装; - 根据相应服务来得到
channel
包装; - 根据
channel = channelWrapper.getChannelObjectPool().borrowObject();
得到channel
- 将请求写入
channel
并刷出去channel.writeAndFlush(request);
- 然后:(看里面代码注释即可,已经很详细了)
1 |
|
客户端接口的动态代理逻辑
由上面实现的CglibRpcProxy
通过newInstance()
得到个实例,并调用其相关实现
1 |
|
最后,客户端关闭逻辑:
1 |
|
具体代码:
1 | package com.nia.rpc.core.client; |
4,对外提供封装调用接口
在这里顺带教大家一个链式编程风格的设计,具体参考:Java里实现链式编程风格
服务端:ServerBuilder
代码太简单,就不解释了
1 | package com.nia.rpc.core.bootstrap; |
客户端:ClientBuilder
1 | package com.nia.rpc.core.bootstrap; |
至此,migo-RPC的核心模块完成
对核心模块的二次封装,提供更人性化的调用接口migo-rpc-provider
模块
新建子项目,并导入pom
依赖:
1 | <?xml version="1.0" encoding="UTF-8"?> |
分别创建服务端和客户端的工厂bean
:
通过下面代码可以很容易看到所完成的链式风格:
ServerFactoryBean
:
1 | package com.nia.rpc.factory; |
ClientFactoryBean
:
1 | package com.nia.rpc.factory; |
示例使用
这里我们通过一个SpringbootDemo
来演示如何使用:
具体代码结构请看源码
定义一个测试service接口:
1 | package com.nia.rpc.example.service; |
编写其实现类:
1 | package com.nia.rpc.example.service; |
编写Springboot
服务端启动类:
SpringServerConfig
1 | package com.nia.rpc.example.server; |
编写服务调用端启动类:
SpringClientConfig
:
1 | package com.nia.rpc.example.client; |
测试截图: